package com.koron.standard.deviceClasssify.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.koron.standard.deviceClasssify.bean.dto.BaseTypeDTO;
import com.koron.standard.deviceClasssify.bean.dto.TypeDTO;
import com.koron.standard.deviceClasssify.bean.dto.TypeGroupDTO;
import com.koron.standard.deviceClasssify.bean.excel.TypeExcel;
import com.koron.standard.deviceClasssify.bean.po.Type;
import com.koron.standard.deviceClasssify.bean.po.TypeGroupParams;
import com.koron.standard.deviceClasssify.bean.po.TypeParams;
import com.koron.standard.deviceClasssify.enums.NodePositionEnum;
import com.koron.standard.deviceClasssify.enums.TypeLogChangeTypeEnum;
import com.koron.standard.deviceClasssify.enums.TypeLogClassifyEnum;
import com.koron.standard.deviceClasssify.mapper.TypeMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备分类基本信息(Type)表服务实现类
 *
 * @author jzp
 * @since 2021-07-13 17:13:25
 */
@Service
public class TypeService extends ServiceImpl<TypeMapper, Type> {

    /**
     * 默认根节点的层级
     */
    private final Integer ROOT_LEVEL = 1;

    /**
     * 默认第一个节点levelCode
     */
    private final Integer BASE_LEVEL_CODE = 1;

    /**
     * 虚拟跟节点ID
     */
    private final String ROOT_ID = "0";

    @Autowired
    private TypeLogService typeLogService;

    @Autowired
    private TypeParamsService typeParamsService;

    @Autowired
    private TypeGroupService typeGroupService;

    private List<Type> listByParentId(String parentId) {
        LambdaQueryWrapper<Type> wrapper = Wrappers.<Type>lambdaQuery();
        wrapper.eq(Type::getParentId, parentId);
        wrapper.orderByAsc(Type::getCode);
        return list(wrapper);
    }

    private Integer getMaxTypeSort(List<Type> types) {
        return types.stream().map(Type::getSort).max(Comparator.comparingInt(o -> o)).orElse(-1);
    }

    private String getCodeByPath(String path) {
        String[] split = path.split("/");
        StringBuilder code = new StringBuilder();
        for (String s : split) {
            code.append(String.format("%03d", Integer.valueOf(s)));
        }
        return code.toString();
    }

    /**
     * 给节点添加level信息
     */
    private Type getAndSetLevel(String id, Integer parentLevel, String parentLevelPath, Integer levelCode) {
        Type type = new Type();
        type.setId(id);
        if (parentLevel == null) {
            type.setLevel(ROOT_LEVEL);
        } else {
            type.setLevel(parentLevel+1);
        }

        if (levelCode == null) {
            type.setLevelCode(BASE_LEVEL_CODE);
        } else {
            type.setLevelCode(levelCode+BASE_LEVEL_CODE);
        }

        if (parentLevelPath == null) {
            type.setLevelPath(type.getLevelCode().toString());
        } else {
            type.setLevelPath(parentLevelPath + "/" + type.getLevelCode());
        }

        type.setCode(getCodeByPath(type.getLevelPath()));

        return type;
    }

    /**
     * 递归修改当前节点的level信息
     */
    private void updateAllTreeLevel(String id, Integer parentLevel, String parentLevelPath, Integer levelCode) {
        Type currType = getAndSetLevel(id, parentLevel, parentLevelPath, levelCode);
        asyncUpdateById(currType);
        List<Type> types = listByParentId(id);
        for (int i = 0; i < types.size(); i++) {
            updateAllTreeLevel(types.get(i).getId(), currType.getLevel(), currType.getLevelPath(), i);
        }
    }

    /**
     * 修改当前节点下部分[begin,end)节点的level信息
     * begin为null表示所有
     */
    private void updateTreeLevel(String id, Integer begin, Integer end) {
        Type parentType = getById(id);
        // 如果是跟节点
        if (ROOT_ID.equals(id)) {
            parentType = new Type();
        }
        List<Type> types = listByParentId(id);
        if (begin == null) {
            begin = 0;
        }
        if (end == null || end > types.size()) {
            end = types.size();
        }

        for (int i = begin; i < end; i++) {
            updateAllTreeLevel(types.get(i).getId(), parentType.getLevel(), parentType.getLevelPath(), i);
        }
    }

    @Override
    public Type getById(Serializable id) {
        Type type = super.getById(id);
        if (type == null) {
            type = new Type();
            type.setId(id.toString());
        }
        return type;
    }

    /**
     * 根据id真查询，不管有没有被逻辑删除
     */
    public Type getRealById(String id) {
        Type type = getBaseMapper().getRealById(id);
        if (type == null) {
            type = new Type();
            type.setId(id);
        }
        return type;
    }

    public List<Type> getRealByIds(List<String> typeIds) {
        if (CollUtil.isEmpty(typeIds)) {
            return null;
        }
        List<Type> types = getBaseMapper().getRealByIds(typeIds);
        types.forEach(type -> {
            if (type == null) {
                type = new Type();
                type.setId(typeIds.get(types.indexOf(type)));
            }
        });
        return types;
    }

    /**
     * 获取设备分类基本信息，包括技术参数信息
     */
    public TypeDTO getDtoById(String id) {
        TypeDTO typeDTO = new TypeDTO();

        Type type = getRealById(id);
        if (type == null) {
            return null;
        }
        BeanUtils.copyProperties(type, typeDTO);

        List<TypeParams> typeParamsList = typeParamsService.listByTypeId(id);

        typeDTO.setTypeParamsList(typeParamsList);
        return typeDTO;
    }

    /**
     * 获取设备分类基本信息，包括技术参数，基本参数对应的参数组中的预设参数和自定义参数
     */
    public BaseTypeDTO getBaseDtoById(String id) {
        TypeDTO typeDTO = getDtoById(id);

        BaseTypeDTO baseTypeDTO = new BaseTypeDTO();
        BeanUtils.copyProperties(typeDTO, baseTypeDTO);

        String groupId = typeDTO.getGroupId();
        TypeGroupDTO typeGroupDTO = typeGroupService.getDtoById(groupId);

        if (typeGroupDTO == null) {
            return baseTypeDTO;
        }
        Collection<TypeGroupParams> typeGroupParams = CollUtil.addAll(typeGroupDTO.getTypeGroupDefaultParamsList(), typeGroupDTO.getTypeGroupCustomParamsList());

        baseTypeDTO.setGroupName(typeGroupDTO.getName());
        baseTypeDTO.setTypeGroupParamsList(typeGroupParams);
        return baseTypeDTO;
    }

    /**
     * 根据分类编码查询分类信息
     */
    public Type getByCode(String code) {
        LambdaQueryWrapper<Type> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Type::getCode, code);
        return getOne(wrapper, false);
    }

    /**
     * 根据多个编码查询其分类信息
     */
    public List<Type> listIdsByCodes(List<String> codes) {
        LambdaQueryWrapper<Type> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Type::getCode, codes);
        return list(wrapper);
    }

    /**
     * 查询除所有的分类编码并去重
     */
    public Set<String> listAllCode() {
        List<Type> types = getBaseMapper().selectList(
                Wrappers.<Type>lambdaQuery()
                        .select(Type::getCode)
        );
        return types.stream().map(Type::getCode).collect(Collectors.toSet());
    }

    @Override
    public boolean save(Type type) {
        String parentId = type.getParentId();

        String parentLevelPath = "";
        // 设置层级为父级层级+1
        if (parentId == null || ROOT_ID.equals(parentId)) {
            type.setParentId(ROOT_ID);
            type.setParentName("0");
            type.setLevel(ROOT_LEVEL);
        } else {
            Type parentType = getById(parentId);
            type.setLevel(parentType.getLevel()+1);
            parentLevelPath = parentType.getLevelPath()+"/";
        }

        // 设置parentName
        if (StrUtil.isEmpty(type.getParentName())) {
            Type parentType = getById(parentId);
            type.setParentName(parentType.getName());
        }

        // 设置层级编码等
        List<Type> types = listByParentId(parentId);
        type.setSort(getMaxTypeSort(types)+1);
        type.setLevelCode(types.size()+1);
        type.setLevelPath(parentLevelPath+type.getLevelCode());
        type.setCode(getCodeByPath(type.getLevelPath()));

        return super.save(type);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void saveOrUpdateDto(TypeDTO typeDTO) {
        // 新增时分类名称不能重复
        if (typeDTO.getId() == null) {
            List<Type> types = listByParentId(typeDTO.getParentId());
            long count = types.stream().filter(e -> e.getName().equals(typeDTO.getName())).count();
            if (count != 0) {
                throw new IllegalArgumentException("分类名称不能重复");
            }
            typeLogService.save(TypeLogClassifyEnum.TYPE, typeDTO.getId(), typeDTO.getName(), TypeLogChangeTypeEnum.ADD);
            save(typeDTO);
        } else {
            typeLogService.save(TypeLogClassifyEnum.TYPE, typeDTO.getId(), typeDTO.getName(), TypeLogChangeTypeEnum.UPDATE);
            saveOrUpdate(typeDTO);
        }

        typeParamsService.saveParams(typeDTO.getTypeParamsList(), typeDTO.getId());

    }

    public void asyncUpdateById(Type entity) {
        // 暂时先不异步
        super.updateById(entity);
    }

    public void deleteDto(String id) {
        List<Type> types = listByParentId(id);
        if (types.size() > 0) {
            throw new IllegalArgumentException("该节点下还有子节点，不能删除");
        }

        // 1.删除节点, 2.删除对应的技术参数, 3.调整节点
        Type type = getById(id);
        // 所有兄弟节点
        List<Type> siblingTypes = listByParentId(type.getParentId());
        // 节点的位置
        int index = siblingTypes.indexOf(type);

        removeById(id);
        typeLogService.save(TypeLogClassifyEnum.TYPE, type.getId(), type.getName(), TypeLogChangeTypeEnum.DELETE);
        typeParamsService.removeByTypeId(id);

        // 调整节点
        // 更新节点所有弟弟节点的level信息
        updateTreeLevel(type.getParentId(), index, null);
    }

    /**
     * 拖拽设备分类树节点
     * @param id 被拖拽节点对应的节点id
     * @param targetId 结束拖拽时最后进入的节点id
     * @param position 被拖拽节点的放置位置（before、after、inner）
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void drag(String id, String targetId, String position) {
        // 源节点
        Type sourceType = getById(id);
        // 目标节点
        Type target = getById(targetId);
        // 源节点的所有兄弟节点
        List<Type> sourceSiblingTypes = listByParentId(sourceType.getParentId());
        // 目标节点的所有子节点
        List<Type> targetList = listByParentId(targetId);
        // 源节点的位置，使用indexOf必须重写equals
        int sourceIndex = sourceSiblingTypes.indexOf(sourceType);

        // 如果是inner
        if (NodePositionEnum.INNER.getPosition().equals(position)) {
            // 源节点的父节点
            Type sourceParentType = getById(sourceType.getParentId());

            // 将当前节点移动到目标节点下最后一个子节点
            Type type = getAndSetLevel(id, target.getLevel(), target.getLevelPath(), targetList.size());
            type.setParentId(targetId);
            type.setSort(getMaxTypeSort(targetList)+1);
            asyncUpdateById(type);

            // 更新当前节点下所有节点的level信息
            updateTreeLevel(id, null, null);
            // 更新源节点所有弟弟节点的level信息
            updateTreeLevel(sourceParentType.getId(), sourceIndex, null);
        } else {
            // 如果源节点和目标节点不是兄弟节点，那么得先inner再移动
            if (!StrUtil.equals(sourceType.getParentId(), target.getParentId())) {
                drag(id, target.getParentId(), NodePositionEnum.INNER.getPosition());
                drag(id, targetId, position);
                return;
            }

            // 目标节点的位置，使用indexOf必须重写equals
            int targetIndex = sourceSiblingTypes.indexOf(target);

            int min = Math.min(sourceIndex, targetIndex);
            int max = Math.max(sourceIndex, targetIndex);

            List<Type> subTypes = sourceSiblingTypes.subList(min, max);
            int targetSort = target.getSort();

            // 往前面移动
            if (targetIndex < sourceIndex) {
                for (Type subType : subTypes) {
                    Type type = new Type();
                    type.setId(subType.getId());
                    // 如果目标节点在源节点之前，所有sort+1
                    type.setSort(subType.getSort() + 1);
                    asyncUpdateById(type);
                }
                // 节点移动到后面
                if (NodePositionEnum.AFTER.getPosition().equals(position)) {
                    Type type = new Type();
                    // 如果移动到后面，源节点为目标节点位置+1
                    type.setId(sourceType.getId());
                    type.setSort(targetSort+1);
                    asyncUpdateById(type);
                    // 目标节点位置-1（之前被+1了，所以还原）
                    type.setId(target.getId());
                    type.setSort(targetSort);
                    asyncUpdateById(type);
                } else if (NodePositionEnum.BEFORE.getPosition().equals(position)) {
                    // 如果移动到前面，源节点位置为目标节点位置
                    Type type = new Type();
                    type.setId(sourceType.getId());
                    type.setSort(targetSort);
                    asyncUpdateById(type);
                }
            } else {// 往后面移动
                for (Type subType : subTypes) {
                    Type type = new Type();
                    type.setId(subType.getId());
                    // 如果目标节点在源节点之后，所有sort-1
                    type.setSort(subType.getSort() - 1);
                    asyncUpdateById(type);
                }
                // 节点移动到后面
                if (NodePositionEnum.AFTER.getPosition().equals(position)) {
                    Type type = new Type();
                    // 如果移动到后面，源节点为目标节点位置
                    type.setId(sourceType.getId());
                    type.setSort(targetSort);
                    asyncUpdateById(type);
                    // 目标节点位置-1
                    type.setId(target.getId());
                    type.setSort(targetSort-1);
                    asyncUpdateById(type);
                } else if (NodePositionEnum.BEFORE.getPosition().equals(position)) {
                    // 如果移动到前面，源节点位置为目标节点位置-1
                    Type type = new Type();
                    type.setId(sourceType.getId());
                    type.setSort(targetSort-1);
                    asyncUpdateById(type);
                }
            }

            if (max < sourceSiblingTypes.size()) {
                max++;
            }
            // 更新中间部分兄弟的level信息
            updateTreeLevel(sourceType.getParentId(), min, max);
        }

    }

    public boolean isRefGroup(String groupId) {
        LambdaQueryWrapper<Type> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Type::getGroupId, groupId);
        Type one = getOne(wrapper, false);
        return one != null;
    }

    public List<TypeExcel> listTypeExcel(String id) {
        List<TypeExcel> result = new ArrayList<>();
        // 查询所有的设备分类数据
        List<Type> types = new ArrayList<>();
        if (StrUtil.isEmpty(id)) {
            types = list();
        } else {
            Type parentType = getById(id);
            types.add(parentType);
            types.addAll(listByParentId(id));
        }
        // 每50个id查询一次技术参数数据
        for (int i = 0; i < types.size(); i = i + 50) {
            List<Type> subTypes = types.subList(i, Math.min((i + 50), types.size()));
            Map<String, Type> typeMap = subTypes.stream().collect(Collectors.toMap(Type::getId, e -> e, (o1, o2) -> o1, LinkedHashMap::new));
            Map<String, List<TypeParams>> typeParamsMap = typeParamsService.listByTypeIds(new ArrayList<>(typeMap.keySet()))
                    .stream()
                    .collect(Collectors.groupingBy(TypeParams::getTypeId, LinkedHashMap::new, Collectors.toList()));
            typeMap.forEach((typeId, type) -> {
                List<TypeExcel> typeExcelList = new ArrayList<>();
                for (TypeParams typeParams : Optional.ofNullable(typeParamsMap.get(typeId)).orElse(new ArrayList<>())) {
                    TypeExcel typeExcel = new TypeExcel();
                    typeExcel.setOrder(Optional.ofNullable(type.getLevelPath()).map(e -> e.replaceAll("/", ".")).orElse(""));
                    typeExcel.setCode(type.getCode());
                    typeExcel.setName(type.getName());
                    typeExcel.setIsVirtual(Objects.toString(type.getIsVirtual(), ""));
                    typeExcel.setMajor(type.getMajor());
                    typeExcel.setGroupId(type.getGroupId());
                    typeExcel.setParamName(typeParams.getParamName());
                    typeExcel.setDataType(typeParams.getDataType());
                    typeExcel.setDataLength(Objects.toString(typeParams.getDataLength(), ""));
                    typeExcel.setDataPrecision(Objects.toString(typeParams.getDataPrecision(), ""));
                    typeExcel.setEnterType(typeParams.getEnterType());
                    typeExcel.setIsMust(Objects.toString(typeParams.getIsMust(), ""));
                    typeExcel.setDataUnit(typeParams.getDataUnit());
                    typeExcel.setDesc(typeParams.getDesc());
                    typeExcel.setDataText(typeParams.getDataText());
                    typeExcelList.add(typeExcel);
                }
                // 如果技术参数为空，则值输出基本信息
                if (typeExcelList.size() == 0) {
                    TypeExcel typeExcel = new TypeExcel();
                            typeExcel.setOrder(Optional.ofNullable(type.getLevelPath()).map(e -> e.replaceAll("/", ".")).orElse(""));
                            typeExcel.setCode(type.getCode());
                            typeExcel.setName(type.getName());
                            typeExcel.setIsVirtual(Objects.toString(type.getIsVirtual(), ""));
                            typeExcel.setMajor(type.getMajor());
                            typeExcel.setGroupId(type.getGroupId());
                    typeExcelList.add(typeExcel);
                }
                result.addAll(typeExcelList);
            });
        }
        return result;
    }

    public void fixParams() {
        typeParamsService.fixParams();
    }
}
